#pragma once

#include <limits.h>

#include "Types.h"
#include "TLSFBlockHeaderHandler.h"


namespace MemMgr
{
	class TLSFAllocator
	{
	public:

		typedef TLSFBlockHeaderHandler::Word Word;
		typedef Word BitMap;

		static const size_t WORD_NUM_BIT = sizeof(Word) * CHAR_BIT;
		static const size_t MIN_HANDLED_SIZE = 0x20;
		static const size_t FIRST_LEVEL_MIN_INDEX;
		static const size_t SPLIT_SIZE_THRESHOLD = MIN_HANDLED_SIZE + TLSFBlockHeaderHandler::HEADER_BYTES_OVERHEAD;

		static TLSFAllocator &GetSingleton()
		{
			static TLSFAllocator instance;
			return instance;
		}

		~TLSFAllocator(void);

		void Initialize(void* iPoolPtr, const size_t iPoolSize, const size_t iLog2SecondLevelSplit = 0x05);
		void* Allocate(const size_t iSize);
		void Deallocate(void* iPtr);

	private:
		
		TLSFAllocator(void);

		TLSFAllocator(const TLSFAllocator& iOther); // Not implemented
		TLSFAllocator& operator=(const TLSFAllocator& iOther); // Not implemented

		void RetrieveInsertionMapping(const size_t iSize, size_t& oFirstLevelIndex, size_t& oSecondLevelIndex) const;
		void RetrieveSearchMapping(size_t& ioSize, size_t& oFirstLevelIndex, size_t& oSecondLevelIndex) const;

		size_t GetOneDimensionIndex(const size_t iFirstLevelIndex, const size_t iSecondLevelIndex) const;

		Word* FindSuitableBlock(size_t& ioFirstLevelIndex, size_t& ioSecondLevelIndex) const;
		void InsertBlock(Word* ioBlock, const size_t iFirstLevelIndex, const size_t iSecondLevelIndex);
		Word* DetachHeadBlock(const size_t iFirstLevelIndex, const size_t iSecondLevelIndex);
		void DetachBlock(Word* ioBlock, const size_t iFirstLevelIndex, const size_t iSecondLevelIndex);
		Word* SplitBlock(Word* iBlock, const size_t iSize);

		void Merge(Word* iFirstBlock, Word* iSecondBlock);
		Word* MergeWithPrevPhysicBlock(Word* iBlock);
		Word* MergeWithNextPhysicBlock(Word* iBlock);

		bool FindSecondLevelAvailable(const size_t iFirstLevelIndex, size_t& ioSecondLevelIndex) const;

		void SetBitAvailable(const size_t iFirstLevelIndex, const size_t iSecondLevelIndex);
		void SetBitUnavailable(const size_t iFirstLevelIndex, const size_t iSecondLevelIndex);

		size_t RetrieveLowerBoundSize(const size_t iSize) const;

		// For debug purpose only
		void TLSFAllocator::IntegrityCheck(void);

		Word** mChunks;
		char* mMemoryPool;
		BitMap mSecondLevelBitmaps[WORD_NUM_BIT];
		BitMap mFirstLevelBitmap;

		size_t mSecondLevelSplit;
		size_t mLog2SecondLevelSplit;

	};
};